#!/usr/bin/env python
"""
##############################################################################
用命令行和可复用的启动方案来启动Python程序；在命令行开头自动向Python可执行文件插入"python"
或路径；这个模块的某个部分可能假定Python在你的系统路径中。

使用subprocess模块也行，不过os.popen在内部调用这个模块，目标是在这里启动一个独立运行的程序
，而非连接到它的流；multiprocessing模块也是一个选择，不过这里处理命令行而非函数，为实现这里
的选项之一而开始一个进程不是很合理。
##############################################################################
"""

import sys
import os

PYFILE = (sys.platform[:3] == 'win' and 'python.exe') or 'python'
# and 和 or 运算
# 判断变量是否为0， 是0则为False，非0判断为True，
# and中含0，返回0； 均为非0时，返回后一个值， 
# 2 and 0   # 返回0
# 2 and 1   # 返回1
# 1 and 2   # 返回2

# or中， 至少有一个非0时，返回第一个非0,
# 2 or 0   # 返回2
# 2 or 1   # 返回2
# 0 or 1   # 返回1 
PYPATH = sys.executable


def fixWindowsPath(cmdline):
	"""
	将cmdline开头的脚本文件名路径中所有'/'改为'\\'；在Windows下，仅为运行需要处理这种
	工具的类所使用；在其他平台上，这么做也没有坏处
	"""
	splitline = cmdline.lstrip().split(' ')  # 在空格处分割字符串
	fixedpath = os.path.normpath(splitline[0])  # 解决斜杠问题
	return ' '.join([fixedpath] + splitline[1:])  # 把路径重新拼起来


class LaunchMode:
	"""
	在实例中待命，声明标签并运行命令；子类按照run方法中的需要格式化命令行；命令应当以准
	备运行的Python脚本名开头，而且不带“python”或脚本的完整路径。
	"""

	def __init__(self, label, command):
		self.what = label
		self.where = command

	def __call__(self):  # 等待调用，执行按钮按下的回调工作
		self.announce(self.what)
		self.run(self.where)  # 子类必须定义run

	def announce(self, text):  # 子类可以重新定义announce
		print(text)  # 用方法代替if、elif逻辑

	def run(self, cmdline):
		assert False, 'run must be defined'


class System(LaunchMode):
	"""
	运行shell命令行中指定的Python脚本；小心，可能会阻塞调用者，除非在Unix下带上&操
	作符。
	"""

	def run(self, cmdline):
		cmdline = fixWindowsPath(cmdline)
		os.system('{} {}'.format(PYPATH, cmdline))


class Popen(LaunchMode):
	"""
	在新进程中运行shell命令行；小心，可能会阻塞其调用者，因为管道闭得太快
	"""

	def run(self, cmdline):
		cmdline = fixWindowsPath(cmdline)
		os.popen(PYPATH + ' ' + cmdline)  # 假设没有数据可以进行读取


class Fork(LaunchMode):
	"""
	在显示地创建新进程中运行命令，仅在类Unix系统下可用，包括Cygwin
	"""

	def run(self, cmdline):
		assert hasattr(os, 'fork'), 'platform not supported'
		cmdline = cmdline.split()  # 把字符串转换为列表
		if os.fork() == 0:  # 开始新的子进程
			os.execvp(PYPATH, [PYFILE] + cmdline)  # 在子进程中运行新程序


class Start(LaunchMode):
	"""
	独立于调用者运行程序；仅在Windows下可用：使用了文件名关联
	"""

	def run(self, cmdline):
		assert sys.platform[:3] == 'win', 'platform not supported'
		cmdline = fixWindowsPath(cmdline)
		os.startfile(cmdline)


class StartArgs(LaunchMode):
	"""
	仅在Windows下可用：args可能需要用到真正的start命令；斜杠在这里没问题。
	"""

	def run(self, cmdline):
		assert sys.platform[:3] == 'win', 'platform not supported'
		os.system('start ' + cmdline)  # 可能会创建弹出窗口


class Spawn(LaunchMode):
	"""
	在独立于调用者的新进程中运行Python；在Windows和Unix下都可用；DOS中使用
	P_NOWAIT；斜杠在这里没问题。
	"""

	def run(self, cmdline):
		os.spawnv(os.P_DETACH, PYPATH, (PYPATH, cmdline))


class Top_level(LaunchMode):
	"""
	在新窗口中运行，进程是同一个；待讨论：还需要GUI类信息。
	"""

	def run(self, cmdline):
		assert False, 'Sorry - mode not yet implemented'


#############################
# 为这个平台挑选一个最佳的启动器 #
# 可能需要在其他地方细化这个选项 #
#############################

if sys.platform[:3] == 'win':
	PortableLauncher = Spawn
else:
	PortableLauncher = Fork


class QuietPortableLauncher(PortableLauncher):
	"""
	不通知安静地用平台适用的方式启动程序
	"""
	
	def announce(self, text):
		pass


def selftest():
	file = 'echo.py'
	input('default mode...')
	PortableLauncher(file, file)()  # 不阻塞

	input('system mode...')
	System(file, file)()  # 阻塞

	if sys.platform[:3] == 'win':
		input('DOS start mode...')
		StartArgs(file, file)()  #不阻塞


if __name__ == '__main__':
	selftest()
